internal static Expression Parse(ParseInfo state, ref int index, FunctionKind kind) { string code = state.Code; int position = index; switch (kind) { case FunctionKind.AsyncAnonymousFunction: case FunctionKind.AnonymousFunction: case FunctionKind.AnonymousGenerator: { break; } case FunctionKind.Function: { if (!Parser.Validate(code, "function", ref position)) { return(null); } if (code[position] == '*') { kind = FunctionKind.Generator; position++; } else if ((code[position] != '(') && (!Tools.IsWhiteSpace(code[position]))) { return(null); } break; } case FunctionKind.Getter: { if (!Parser.Validate(code, "get ", ref position)) { return(null); } break; } case FunctionKind.Setter: { if (!Parser.Validate(code, "set ", ref position)) { return(null); } break; } case FunctionKind.AsyncMethod: { break; } case FunctionKind.MethodGenerator: case FunctionKind.Method: { if (code[position] == '*') { kind = FunctionKind.MethodGenerator; position++; } else if (kind == FunctionKind.MethodGenerator) { throw new ArgumentException("mode"); } break; } case FunctionKind.AsyncArrow: { if (!Parser.Validate(code, "async", ref position)) { return(null); } break; } case FunctionKind.Arrow: { break; } case FunctionKind.AsyncFunction: { if (!Parser.Validate(code, "async", ref position)) { return(null); } Tools.SkipSpaces(code, ref position); if (!Parser.Validate(code, "function", ref position)) { return(null); } break; } default: throw new NotImplementedException(kind.ToString()); } Tools.SkipSpaces(state.Code, ref position); var parameters = new List <ParameterDescriptor>(); CodeBlock body = null; string name = null; bool arrowWithSinglePrm = false; int nameStartPos = 0; bool containsDestructuringPrms = false; if (kind != FunctionKind.Arrow) { if (code[position] != '(') { nameStartPos = position; if (Parser.ValidateName(code, ref position, false, true, state.Strict)) { name = Tools.Unescape(code.Substring(nameStartPos, position - nameStartPos), state.Strict); } else if ((kind == FunctionKind.Getter || kind == FunctionKind.Setter) && Parser.ValidateString(code, ref position, false)) { name = Tools.Unescape(code.Substring(nameStartPos + 1, position - nameStartPos - 2), state.Strict); } else if ((kind == FunctionKind.Getter || kind == FunctionKind.Setter) && Parser.ValidateNumber(code, ref position)) { name = Tools.Unescape(code.Substring(nameStartPos, position - nameStartPos), state.Strict); } else { ExceptionHelper.ThrowSyntaxError("Invalid function name", code, nameStartPos, position - nameStartPos); } Tools.SkipSpaces(code, ref position); if (code[position] != '(') { ExceptionHelper.ThrowUnknownToken(code, position); } } else if (kind == FunctionKind.Getter || kind == FunctionKind.Setter) { ExceptionHelper.ThrowSyntaxError("Getter and Setter must have name", code, index); } else if (kind == FunctionKind.Method || kind == FunctionKind.MethodGenerator || kind == FunctionKind.AsyncMethod) { ExceptionHelper.ThrowSyntaxError("Method must have name", code, index); } position++; } else if (code[position] != '(') { arrowWithSinglePrm = true; } else { position++; } Tools.SkipSpaces(code, ref position); if (code[position] == ',') { ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, code, position); } while (code[position] != ')') { if (parameters.Count == 255 || (kind == FunctionKind.Setter && parameters.Count == 1) || kind == FunctionKind.Getter) { ExceptionHelper.ThrowSyntaxError(string.Format(Strings.TooManyArgumentsForFunction, name), code, index); } bool rest = Parser.Validate(code, "...", ref position); Expression destructor = null; int n = position; if (!Parser.ValidateName(code, ref position, state.Strict)) { if (code[position] == '{') { destructor = (Expression)ObjectDefinition.Parse(state, ref position); } else if (code[position] == '[') { destructor = (Expression)ArrayDefinition.Parse(state, ref position); } if (destructor == null) { ExceptionHelper.ThrowUnknownToken(code, nameStartPos); } containsDestructuringPrms = true; } var pname = Tools.Unescape(code.Substring(n, position - n), state.Strict); var reference = new ParameterReference(pname, rest, state.LexicalScopeLevel + 1) { Position = n, Length = position - n }; var desc = reference.Descriptor as ParameterDescriptor; if (destructor != null) { desc.Destructor = new ObjectDesctructor(destructor); } parameters.Add(desc); Tools.SkipSpaces(state.Code, ref position); if (arrowWithSinglePrm) { position--; break; } if (code[position] == '=') { if (rest) { ExceptionHelper.ThrowSyntaxError("Rest parameters can not have an initializer", code, position); } do { position++; }while (Tools.IsWhiteSpace(code[position])); desc.initializer = ExpressionTree.Parse(state, ref position, false, false) as Expression; } if (code[position] == ',') { if (rest) { ExceptionHelper.ThrowSyntaxError("Rest parameters must be the last in parameters list", code, position); } do { position++; }while (Tools.IsWhiteSpace(code[position])); } } if (kind == FunctionKind.Setter) { if (parameters.Count != 1) { ExceptionHelper.ThrowSyntaxError("Setter must has only one argument", code, index); } } position++; Tools.SkipSpaces(code, ref position); if (kind == FunctionKind.Arrow || kind == FunctionKind.AsyncArrow) { if (!Parser.Validate(code, "=>", ref position)) { ExceptionHelper.ThrowSyntaxError("Expected \"=>\"", code, position); } Tools.SkipSpaces(code, ref position); } if (code[position] != '{') { var oldFunctionScopeLevel = state.FunctionScopeLevel; state.FunctionScopeLevel = ++state.LexicalScopeLevel; try { if (kind == FunctionKind.Arrow || kind == FunctionKind.AsyncArrow) { body = new CodeBlock(new CodeNode[] { new Return(ExpressionTree.Parse(state, ref position, processComma: false) as Expression) }) { _variables = new VariableDescriptor[0] }; body.Position = body._lines[0].Position; body.Length = body._lines[0].Length; } else { ExceptionHelper.ThrowUnknownToken(code, position); } } finally { state.FunctionScopeLevel = oldFunctionScopeLevel; state.LexicalScopeLevel--; } } else { using (state.WithNewLabelsScope()) using (state.WithCodeContext()) { if (kind == FunctionKind.Generator || kind == FunctionKind.MethodGenerator || kind == FunctionKind.AnonymousGenerator) { state.CodeContext |= CodeContext.InGenerator; } else if (kind == FunctionKind.AsyncFunction || kind == FunctionKind.AsyncMethod || kind == FunctionKind.AsyncAnonymousFunction || kind == FunctionKind.AsyncArrow) { state.CodeContext |= CodeContext.InAsync; } state.CodeContext |= CodeContext.InFunction; state.CodeContext |= CodeContext.AllowDirectives; state.CodeContext &= ~(CodeContext.InExpression | CodeContext.Conditional | CodeContext.InEval); state.AllowReturn++; try { state.AllowBreak.Push(false); state.AllowContinue.Push(false); body = CodeBlock.Parse(state, ref position) as CodeBlock; if (containsDestructuringPrms) { var destructuringTargets = new List <VariableDescriptor>(); var assignments = new List <Expression>(); for (var i = 0; i < parameters.Count; i++) { if (parameters[i].Destructor != null) { var targets = parameters[i].Destructor.GetTargetVariables(); for (var j = 0; j < targets.Count; j++) { destructuringTargets.Add(new VariableDescriptor(targets[j].Name, state.FunctionScopeLevel)); } assignments.Add(new Assignment(parameters[i].Destructor, parameters[i].references[0])); } } var newLines = new CodeNode[body._lines.Length + 1]; System.Array.Copy(body._lines, 0, newLines, 1, body._lines.Length); newLines[0] = new VariableDefinition(destructuringTargets.ToArray(), assignments.ToArray(), VariableKind.AutoGeneratedParameters); body._lines = newLines; } } finally { state.AllowBreak.Pop(); state.AllowContinue.Pop(); state.AllowReturn--; } if (kind == FunctionKind.Function && string.IsNullOrEmpty(name)) { kind = FunctionKind.AnonymousFunction; } } } if (body._strict || (parameters.Count > 0 && parameters[parameters.Count - 1].IsRest) || kind == FunctionKind.Arrow) { for (var j = parameters.Count; j-- > 1;) { for (var k = j; k-- > 0;) { if (parameters[j].Name == parameters[k].Name) { ExceptionHelper.ThrowSyntaxError("Duplicate names of function parameters not allowed in strict mode", code, index); } } } if (name == "arguments" || name == "eval") { ExceptionHelper.ThrowSyntaxError("Functions name can not be \"arguments\" or \"eval\" in strict mode at", code, index); } for (int j = parameters.Count; j-- > 0;) { if (parameters[j].Name == "arguments" || parameters[j].Name == "eval") { ExceptionHelper.ThrowSyntaxError("Parameters name cannot be \"arguments\" or \"eval\" in strict mode at", code, parameters[j].references[0].Position, parameters[j].references[0].Length); } } } var func = new FunctionDefinition(name) { parameters = parameters.ToArray(), _body = body, kind = kind, Position = index, Length = position - index, #if DEBUG trace = body.directives != null?body.directives.Contains("debug trace") : false #endif }; if (!string.IsNullOrEmpty(name)) { func.Reference.ScopeLevel = state.LexicalScopeLevel; func.Reference.Position = nameStartPos; func.Reference.Length = name.Length; func.reference._descriptor.definitionScopeLevel = func.reference.ScopeLevel; } if (parameters.Count != 0) { var newVariablesCount = body._variables.Length + parameters.Count; for (var j = 0; j < body._variables.Length; j++) { for (var k = 0; k < parameters.Count; k++) { if (body._variables[j].name == parameters[k].name) { newVariablesCount--; break; } } } var newVariables = new VariableDescriptor[newVariablesCount]; for (var j = 0; j < parameters.Count; j++) { newVariables[j] = parameters[parameters.Count - j - 1]; // порядок определяет приоритет for (var k = 0; k < body._variables.Length; k++) { if (body._variables[k] != null && body._variables[k].name == parameters[j].name) { if (body._variables[k].initializer != null) { newVariables[j] = body._variables[k]; } else { body._variables[k].lexicalScope = false; } body._variables[k] = null; break; } } } for (int j = 0, k = parameters.Count; j < body._variables.Length; j++) { if (body._variables[j] != null) { newVariables[k++] = body._variables[j]; } } body._variables = newVariables; } if ((state.CodeContext & CodeContext.InExpression) == 0 && kind == FunctionKind.Function) // Позволяет делать вызов сразу при объявлении функции // (в таком случае функция не добавляется в контекст). // Если убрать проверку, то в тех сулчаях, // когда определение и вызов стоят внутри выражения, // будет выдано исключение, потому, // что тогда это уже не определение и вызов функции, // а часть выражения, которые не могут начинаться со слова "function". // За красивыми словами "может/не может" кроется другая хрень: если бы это было выражение, // то прямо тут надо было бы разбирать тот оператор, который стоит после определения функции, // что не разумно { var tindex = position; while (position < code.Length && Tools.IsWhiteSpace(code[position]) && !Tools.IsLineTerminator(code[position])) { position++; } if (position < code.Length && code[position] == '(') { var args = new List <Expression>(); position++; for (; ;) { while (Tools.IsWhiteSpace(code[position])) { position++; } if (code[position] == ')') { break; } else if (code[position] == ',') { do { position++; }while (Tools.IsWhiteSpace(code[position])); } args.Add(ExpressionTree.Parse(state, ref position, false, false)); } position++; index = position; while (position < code.Length && Tools.IsWhiteSpace(code[position])) { position++; } if (position < code.Length && code[position] == ';') { ExceptionHelper.Throw((new SyntaxError("Expression can not start with word \"function\""))); } return(new Call(func, args.ToArray())); } else { position = tindex; } } if ((state.CodeContext & CodeContext.InExpression) == 0 && (kind != FunctionKind.Arrow || (state.CodeContext & CodeContext.InEval) == 0)) { if (string.IsNullOrEmpty(name)) { ExceptionHelper.ThrowSyntaxError("Function must has name", state.Code, index); } state.Variables.Add(func.reference._descriptor); } index = position; return(func); }
public override void Optimize(ref CodeNode _this, FunctionDefinition owner, InternalCompilerMessageCallback message, Options opts, FunctionInfo stats) { base.Optimize(ref _this, owner, message, opts, stats); }
public override void Optimize(ref CodeNode _this, FunctionDefinition owner, InternalCompilerMessageCallback message, Options opts, FunctionInfo stats) { baseOptimize(ref _this, owner, message, opts, stats); var vr = _left as VariableReference; if (vr != null) { if (vr._descriptor.IsDefined) { var stype = _right.ResultType; if (vr._descriptor.lastPredictedType == PredictedType.Unknown) { vr._descriptor.lastPredictedType = stype; } else if (vr._descriptor.lastPredictedType != stype) { if (Tools.IsEqual(vr._descriptor.lastPredictedType, stype, PredictedType.Group)) { vr._descriptor.lastPredictedType = stype & PredictedType.Group; } else { if (message != null && stype >= PredictedType.Undefined && vr._descriptor.lastPredictedType >= PredictedType.Undefined) { message(MessageLevel.Warning, Position, Length, "Variable \"" + vr.Name + "\" has ambiguous type. It can be make impossible some optimizations and cause errors."); } vr._descriptor.lastPredictedType = PredictedType.Ambiguous; } } } else if (message != null) { message(MessageLevel.CriticalWarning, Position, Length, "Assign to undefined variable \"" + vr.Name + "\". It will declare a global variable."); } } var variable = _left as Variable; if (variable != null && variable._descriptor.IsDefined && (_codeContext & CodeContext.InWith) == 0 && !variable._descriptor.captured) { if (!stats.ContainsEval && !stats.ContainsWith) { if (owner != null // не будем это применять в корневом узле. Только в функциях. // Иначе это может использоваться как настройка контекста для последующего использования в Eval && (opts & Options.SuppressUselessExpressionsElimination) == 0 && (_codeContext & CodeContext.InLoop) == 0) { if ((owner._body._strict || variable._descriptor.owner != owner || !owner._functionInfo.ContainsArguments)) // аргументы это одна сущность с двумя именами { bool last = true; for (var i = 0; last && i < variable._descriptor.references.Count; i++) { last &= variable._descriptor.references[i].Eliminated || variable._descriptor.references[i].Position <= Position; } if (last) { if (_right.ContextIndependent) { _this.Eliminated = true; _this = Empty.Instance; } else { _this = _right; this._right = null; this.Eliminated = true; this._right = _this as Expression; } } } } } /*if (_this == this && second.ResultInTempContainer) // это присваивание, не последнее, без with * { * _this = new AssignmentOverReplace(first, second) * { * Position = Position, * Length = Length, * _codeContext = _codeContext * }; * }*/ } }
internal static CodeNode Parse(ParseInfo state, ref int index) { if (state.Code[index] != '{') { throw new ArgumentException("Invalid JSON definition"); } var flds = new Dictionary <string, Expression>(); var computedProperties = new List <KeyValuePair <Expression, Expression> >(); int i = index; while (state.Code[i] != '}') { i++; Tools.SkipSpaces(state.Code, ref i); int s = i; if (state.Code[i] == '}') { break; } bool getOrSet = Parser.Validate(state.Code, "get", ref i) || Parser.Validate(state.Code, "set", ref i); Tools.SkipSpaces(state.Code, ref i); if (getOrSet && state.Code[i] == '(') // function with name 'get' or 'set' { getOrSet = false; i = s; } var asterisk = state.Code[i] == '*'; Tools.SkipSpaces(state.Code, ref i); var async = false; if (!asterisk) { async = Parser.Validate(state.Code, "async", ref i); Tools.SkipSpaces(state.Code, ref i); } if (Parser.Validate(state.Code, "[", ref i)) { var name = ExpressionTree.Parse(state, ref i, false, false, false, true, false); while (Tools.IsWhiteSpace(state.Code[i])) { i++; } if (state.Code[i] != ']') { ExceptionHelper.ThrowSyntaxError("Expected ']'", state.Code, i); } do { i++; }while (Tools.IsWhiteSpace(state.Code[i])); Tools.SkipSpaces(state.Code, ref i); CodeNode initializer; if (state.Code[i] == '(') { initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : async ? FunctionKind.AsyncAnonymousFunction : FunctionKind.AnonymousFunction); } else { if (!Parser.Validate(state.Code, ":", ref i)) { ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, state.Code, i); } initializer = ExpressionTree.Parse(state, ref i); } switch (state.Code[s]) { case 'g': { computedProperties.Add(new KeyValuePair <Expression, Expression>(name, new PropertyPair((Expression)initializer, null))); break; } case 's': { computedProperties.Add(new KeyValuePair <Expression, Expression>(name, new PropertyPair(null, (Expression)initializer))); break; } default: { computedProperties.Add(new KeyValuePair <Expression, Expression>(name, (Expression)initializer)); break; } } } else if (getOrSet && state.Code[i] != ':') { i = s; var mode = state.Code[i] == 's' ? FunctionKind.Setter : FunctionKind.Getter; var propertyAccessor = FunctionDefinition.Parse(state, ref i, mode) as FunctionDefinition; var accessorName = propertyAccessor._name; if (!flds.ContainsKey(accessorName)) { var propertyPair = new PropertyPair ( mode == FunctionKind.Getter ? propertyAccessor : null, mode == FunctionKind.Setter ? propertyAccessor : null ); flds.Add(accessorName, propertyPair); } else { var vle = flds[accessorName] as PropertyPair; if (vle == null) { ExceptionHelper.ThrowSyntaxError("Try to define " + mode.ToString().ToLowerInvariant() + " for defined field", state.Code, s); } do { if (mode == FunctionKind.Getter) { if (vle.Getter == null) { vle.Getter = propertyAccessor; break; } } else { if (vle.Setter == null) { vle.Setter = propertyAccessor; break; } } ExceptionHelper.ThrowSyntaxError("Try to redefine " + mode.ToString().ToLowerInvariant() + " of " + propertyAccessor.Name, state.Code, s); }while (false); } } else { if (asterisk) { do { i++; }while (Tools.IsWhiteSpace(state.Code[i])); } i = s; var fieldName = ""; if (Parser.ValidateName(state.Code, ref i, false, true, state.Strict)) { fieldName = Tools.Unescape(state.Code.Substring(s, i - s), state.Strict); } else if (Parser.ValidateValue(state.Code, ref i)) { if (state.Code[s] == '-') { ExceptionHelper.Throw(new SyntaxError("Invalid char \"-\" at " + CodeCoordinates.FromTextPosition(state.Code, s, 1))); } double d = 0.0; int n = s; if (Tools.ParseNumber(state.Code, ref n, out d)) { fieldName = Tools.DoubleToString(d); } else if (state.Code[s] == '\'' || state.Code[s] == '"') { fieldName = Tools.Unescape(state.Code.Substring(s + 1, i - s - 2), state.Strict); } else if (flds.Count != 0) { ExceptionHelper.Throw((new SyntaxError("Invalid field name at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s)))); } else { return(null); } } else { return(null); } while (Tools.IsWhiteSpace(state.Code[i])) { i++; } Expression initializer = null; if (state.Code[i] == '(') { i = s; initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.MethodGenerator : async ? FunctionKind.AsyncMethod : FunctionKind.Method); } else { if (asterisk || (async && state.Code[i] != ':')) { ExceptionHelper.ThrowSyntaxError("Unexpected token", state.Code, i); } if (state.Code[i] != ':' && state.Code[i] != ',' && state.Code[i] != '}') { ExceptionHelper.ThrowSyntaxError("Expected ',', ';' or '}'", state.Code, i); } Expression aei = null; if (flds.TryGetValue(fieldName, out aei)) { if (state.Strict ? (!(aei is Constant) || (aei as Constant).value != JSValue.undefined) : aei is PropertyPair) { ExceptionHelper.ThrowSyntaxError("Try to redefine field \"" + fieldName + "\"", state.Code, s, i - s); } if (state.Message != null) { state.Message(MessageLevel.Warning, i, 0, "Duplicate key \"" + fieldName + "\""); } } if (state.Code[i] == ',' || state.Code[i] == '}') { if (!Parser.ValidateName(fieldName, 0)) { ExceptionHelper.ThrowSyntaxError("Invalid variable name", state.Code, i); } initializer = new Variable(fieldName, state.LexicalScopeLevel); } else { do { i++; }while (Tools.IsWhiteSpace(state.Code[i])); initializer = ExpressionTree.Parse(state, ref i, false, false); } } flds[fieldName] = initializer; } while (Tools.IsWhiteSpace(state.Code[i])) { i++; } if ((state.Code[i] != ',') && (state.Code[i] != '}')) { return(null); } } i++; var pos = index; index = i; return(new ObjectDefinition(flds, computedProperties.ToArray()) { Position = pos, Length = index - pos }); }
public ClassConstructor(Context context, FunctionDefinition creator, ClassDefinition classDefinition) : base(context, creator) { this.classDefinition = classDefinition; }
internal static CodeNode Parse(ParseInfo state, ref int index) { string code = state.Code; int i = index; if (!Parser.Validate(code, "class", ref i)) { return(null); } while (Tools.IsWhiteSpace(code[i])) { i++; } string name = null; Expression baseType = null; if (!Parser.Validate(code, "extends ", i)) { var n = i; if (Parser.ValidateName(code, ref i, true)) { name = code.Substring(n, i - n); } while (Tools.IsWhiteSpace(code[i])) { i++; } } if (Parser.Validate(code, "extends ", ref i)) { var n = i; if (!Parser.ValidateName(code, ref i, true) && !Parser.Validate(code, "null", ref i)) { ExceptionHelper.ThrowSyntaxError("Invalid base class name", state.Code, i); } var baseClassName = code.Substring(n, i - n); if (baseClassName == "null") { baseType = new Constant(JSValue.@null); } else { baseType = new Variable(baseClassName, 1); } baseType.Position = n; baseType.Length = i - n; while (Tools.IsWhiteSpace(code[i])) { i++; } } if (code[i] != '{') { ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, code, i); } FunctionDefinition ctor = null; ClassDefinition result = null; var oldStrict = state.strict; state.strict = true; var flds = new Dictionary <string, MemberDescriptor>(); var computedProperties = new List <MemberDescriptor>(); var oldCodeContext = state.CodeContext; state.CodeContext |= CodeContext.InExpression; try { while (code[i] != '}') { do { i++; }while (Tools.IsWhiteSpace(code[i]) || code[i] == ';'); int s = i; if (state.Code[i] == '}') { break; } bool @static = Parser.Validate(state.Code, "static", ref i); if (@static) { Tools.SkipSpaces(state.Code, ref i); s = i; } bool getOrSet = Parser.Validate(state.Code, "get", ref i) || Parser.Validate(state.Code, "set", ref i); if (getOrSet) { Tools.SkipSpaces(state.Code, ref i); } var asterisk = state.Code[i] == '*'; if (asterisk) { do { i++; }while (Tools.IsWhiteSpace(state.Code[i])); } if (Parser.Validate(state.Code, "[", ref i)) { var propertyName = ExpressionTree.Parse(state, ref i, false, false, false, true, false); while (Tools.IsWhiteSpace(state.Code[i])) { i++; } if (state.Code[i] != ']') { ExceptionHelper.ThrowSyntaxError("Expected ']'", state.Code, i); } do { i++; }while (Tools.IsWhiteSpace(state.Code[i])); CodeNode initializer; if (state.Code[i] == '(') { initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : FunctionKind.AnonymousFunction); } else { initializer = ExpressionTree.Parse(state, ref i); } switch (state.Code[s]) { case 'g': { computedProperties.Add(new MemberDescriptor((Expression)propertyName, new PropertyPair((Expression)initializer, null), @static)); break; } case 's': { computedProperties.Add(new MemberDescriptor((Expression)propertyName, new PropertyPair(null, (Expression)initializer), @static)); break; } default: { computedProperties.Add(new MemberDescriptor((Expression)propertyName, (Expression)initializer, @static)); break; } } } else if (getOrSet) { i = s; var mode = state.Code[i] == 's' ? FunctionKind.Setter : FunctionKind.Getter; var propertyAccessor = FunctionDefinition.Parse(state, ref i, mode) as FunctionDefinition; var accessorName = (@static ? "static " : "") + propertyAccessor._name; if (!flds.ContainsKey(accessorName)) { var propertyPair = new PropertyPair ( mode == FunctionKind.Getter ? propertyAccessor : null, mode == FunctionKind.Setter ? propertyAccessor : null ); flds.Add(accessorName, new MemberDescriptor(new Constant(propertyAccessor._name), propertyPair, @static)); } else { var vle = flds[accessorName].Value as PropertyPair; if (vle == null) { ExceptionHelper.Throw((new SyntaxError("Try to define " + mode.ToString().ToLowerInvariant() + " for defined field at " + CodeCoordinates.FromTextPosition(state.Code, s, 0)))); } do { if (mode == FunctionKind.Getter) { if (vle.Getter == null) { vle.Getter = propertyAccessor; break; } } else { if (vle.Setter == null) { vle.Setter = propertyAccessor; break; } } ExceptionHelper.ThrowSyntaxError("Try to redefine " + mode.ToString().ToLowerInvariant() + " of " + propertyAccessor.Name, state.Code, s); }while (false); } } else { i = s; string fieldName = null; if (state.Code[i] == '*') { do { i++; }while (Tools.IsWhiteSpace(code[i])); } if (Parser.ValidateName(state.Code, ref i, false, true, state.strict)) { fieldName = Tools.Unescape(state.Code.Substring(s, i - s), state.strict); } else if (Parser.ValidateValue(state.Code, ref i)) { double d = 0.0; int n = s; if (Tools.ParseNumber(state.Code, ref n, out d)) { fieldName = Tools.DoubleToString(d); } else if (state.Code[s] == '\'' || state.Code[s] == '"') { fieldName = Tools.Unescape(state.Code.Substring(s + 1, i - s - 2), state.strict); } } if (fieldName == null) { ExceptionHelper.Throw((new SyntaxError("Invalid member name at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s)))); } if (fieldName == "constructor") { if (@static) { ExceptionHelper.ThrowSyntaxError(Strings.ConstructorCannotBeStatic, state.Code, s); } if (ctor != null) { ExceptionHelper.ThrowSyntaxError("Trying to redefinition constructor", state.Code, s); } state.CodeContext |= CodeContext.InClassConstructor; } else if (@static) { fieldName = "static " + fieldName; state.CodeContext |= CodeContext.InStaticMember; } if (flds.ContainsKey(fieldName)) { ExceptionHelper.Throw(new SyntaxError("Trying to redefinition member \"" + fieldName + "\" at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s))); } state.CodeContext |= CodeContext.InClassDefenition; state.CodeContext &= ~CodeContext.InGenerator; i = s; var method = FunctionDefinition.Parse(state, ref i, FunctionKind.Method) as FunctionDefinition; if (method == null) { ExceptionHelper.ThrowSyntaxError("Unable to parse constructor", state.Code, i); } if (fieldName == "constructor") { ctor = method; } else { flds[fieldName] = new MemberDescriptor(new Constant(method._name), method, @static); } } code = state.Code; } if (ctor == null) { string ctorCode; int ctorIndex = 0; if (baseType != null && !(baseType is Constant)) { ctorCode = "constructor(...args) { super(...args); }"; } else { ctorCode = "constructor(...args) { }"; } ctor = (FunctionDefinition)FunctionDefinition.Parse( new ParseInfo(ctorCode, ctorCode, null) { strict = true, CodeContext = CodeContext.InClassConstructor | CodeContext.InClassDefenition }, ref ctorIndex, FunctionKind.Method); } result = new ClassDefinition(name, baseType, new List <MemberDescriptor>(flds.Values).ToArray(), ctor as FunctionDefinition, computedProperties.ToArray()); if ((oldCodeContext & CodeContext.InExpression) == 0) { if (string.IsNullOrEmpty(name)) { ExceptionHelper.ThrowSyntaxError("Class must have name", state.Code, index); } if (state.strict && state.functionScopeLevel != state.lexicalScopeLevel) { ExceptionHelper.ThrowSyntaxError("In strict mode code, class can only be declared at top level or immediately within other function.", state.Code, index); } state.Variables.Add(result.reference._descriptor); } } finally { state.CodeContext = oldCodeContext; state.strict = oldStrict; } index = i + 1; return(result); }
private ClassDefinition(string name, Expression baseType, MemberDescriptor[] fields, FunctionDefinition ctor, MemberDescriptor[] computedProperties) : base(name) { this._baseClass = baseType; this._constructor = ctor; this._members = fields; this.computedProperties = computedProperties; }
public override void Optimize(ref Core.CodeNode _this, FunctionDefinition owner, CompilerMessageCallback message, Options opts, FunctionInfo stats) { }
public override void Optimize(ref CodeNode _this, FunctionDefinition owner, CompilerMessageCallback message, Options opts, FunctionInfo stats) { if ((opts & Options.SuppressConstantPropogation) == 0 && !_descriptor.captured && _descriptor.isDefined && !stats.ContainsWith && !stats.ContainsEval && (_descriptor.owner != owner || !owner._functionInfo.ContainsArguments)) { var assigns = _descriptor.assignments; if (assigns != null && assigns.Count > 0) { /* * Применение оптимизации зависит от порядка добавления присваиваний. * Этот порядок в свою очередь зависит от порядка следования операций в CodeBlock. * Раньше этот порядок был обратным, сейчас прямой, поэтому здесь присваивания нужно перебирать * в обратном порядке. Оптимизация не применится если найдется изменение в котором first указывает на * это использование. Это говорит о том, что в данном месте этой переменной * присваивается значение */ CodeNode lastAssign = null; for (var i = assigns.Count; i-- > 0;) { if (assigns[i]._left == this || ((assigns[i]._left is AssignmentOperatorCache) && assigns[i]._left._left == this)) { // оптимизация не применяется lastAssign = null; break; } if (assigns[i].Position > Position) { if ((_codeContext & CodeContext.InLoop) != 0 && ((assigns[i] as Expression)._codeContext & CodeContext.InLoop) != 0) // присваивание может быть после этого использования, но если всё это в цикле, то выполнение вернётся сюда. { // оптимизация не применяется lastAssign = null; break; } continue; // пропускаем ноду } if (_descriptor.isReadOnly) { if (assigns[i] is ForceAssignmentOperator) { lastAssign = assigns[i]; break; } } else if (lastAssign == null || assigns[i].Position > lastAssign.Position) { lastAssign = assigns[i]; } } var assign = lastAssign as Assignment; if (assign != null && (assign._codeContext & CodeContext.Conditional) == 0 && assign._right is Constant) { _this = assign._right; } } } }